package at.ac.tuwien.big.we14.lab2.api.impl;

import at.ac.tuwien.big.we14.lab2.api.Category;
import at.ac.tuwien.big.we14.lab2.api.Game;
import at.ac.tuwien.big.we14.lab2.api.PlayersNameVocabulary;
import at.ac.tuwien.big.we14.lab2.api.PlayersStatus;
import at.ac.tuwien.big.we14.lab2.api.PlayersStatus.Status;
import at.ac.tuwien.big.we14.lab2.api.Question;
import at.ac.tuwien.big.we14.lab2.api.QuestionDataProvider;
import at.ac.tuwien.big.we14.lab2.api.QuizFactory;
import static java.lang.System.out;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

public class SimpleGame implements Game{
    
    private String playersId[];
    private String playersName[];
    private List<Question> playedQuestions; /* contains the played question, but maybe not answered */
    private List<History> historyPlayer0; /* History is a Inner class */
    private List<History> historyPlayer1;
    
    /* init a game */
    public SimpleGame () {
    	playersId = new String[2];
        playersName = new String[2];
        playedQuestions = new ArrayList<>();
        historyPlayer0 = new ArrayList<>();
        historyPlayer1 = new ArrayList<>();
    }
    
    @Override
    public String[] getPlayersId() {
        return playersId;
    }
    
    /* set playersid(sessionid) and get random name from the vocabulary */
    @Override
    public void setPlayersId(String[] session_ids, QuizFactory quizfactory) {
        playersId=session_ids;
        PlayersNameVocabulary voc = quizfactory.createPlayersNameVocabulary();
        if(playersName[0]==null && !playersId[0].isEmpty()) {
        	playersName[0]=voc.getPlayerName();
        	playersName[1]="Niemand";
        }
        if(playersName[1].equals("Niemand") && !playersId[1].equals(NONE)) playersName[1]=voc.getPlayerName();
    }
    
    /* a game is over when a games is null terminated. Means the finish page was delivered */
    public boolean isOver(String session_id) {
    	if(session_id.equals(playersId[0])) {
            return (historyPlayer0.size()==QUESTIONS_PER_ROUND*ROUND_TO_PALY+1) ? true :false;
        }
        if(session_id.equals(playersId[1])) {
        	return (historyPlayer1.size()==QUESTIONS_PER_ROUND*ROUND_TO_PALY+1) ? true :false;
        }
    	return false;
    }
   
    @Override
    public void addPlayedQuestion(Question question) {
        playedQuestions.add(question);
    }
    
    /* returns the question for the player */
    @Override
    public Question getQuestionToPaly(String session_id, QuizFactory quizfactory) {
    	/* who is who */
        List<History> myhist;
        List<History> opphist;
        if(session_id.equals(playersId[0])) {
            myhist=historyPlayer0;
            opphist=historyPlayer1;
        } else if(session_id.equals(playersId[1])) {
            myhist=historyPlayer1;
            opphist=historyPlayer0;
        } else return null;
        
        /* Look for played one */
        
        if(playedQuestions.size()>myhist.size()) return playedQuestions.get(myhist.size());
                
        /* New One */
        
        /* Load Data for Bean */
        QuestionDataProvider provider = quizfactory.createQuestionDataProvider();
        List<Category> categorys = provider.loadCategoryData();
        
        
        /* Choose Random Question not played yet */
        Category rand_cat;
        Question rand_question=null;
        
        if(myhist.size()==0) {
        	/* Choose new one  random*/
        	rand_cat = categorys.get(Math.abs((new Random()).nextInt() % categorys.size()));
        	rand_question = rand_cat.getQuestions().get(Math.abs((new Random()).nextInt() % rand_cat.getQuestions().size()));
        	rand_question.setCategory(rand_cat);
        } else {
        	
        	if((myhist.size() % QUESTIONS_PER_ROUND)==0) {
        		/* myhist.size()= 3 6 9 2 15, means new round*/
        		if((opphist.size() % QUESTIONS_PER_ROUND)==0 || (opphist.size()==QUESTIONS_PER_ROUND*ROUND_TO_PALY+1)) {
        			/* if this function gets called in a non needed state null is returned */
        			
        			/* Choose new one  random, but no played category*/
        			List<String> played_cats = getPlayedCategorys();
        			do{
        	        	rand_cat = categorys.get(Math.abs((new Random()).nextInt() % categorys.size()));
        	        }while (played_cats.contains(rand_cat.getName()));
        			rand_question = rand_cat.getQuestions().get(Math.abs((new Random()).nextInt() % rand_cat.getQuestions().size()));
        			rand_question.setCategory(rand_cat);
        		} else return null;
        	} else {
        		/* Choose new one random, but played category*/
	        	for(Category cat : categorys) {
	        		if(cat.getName().equals(myhist.get(myhist.size()-1).getQuestion().getCategory().getName())) {
	        			boolean ok;
	        			int i;
	        			do{
	        				/* choose not played question */
	        				ok=false;
	        				rand_question = cat.getQuestions().get(Math.abs((new Random()).nextInt() % cat.getQuestions().size()));
	        				for(i=(int)Math.ceil(myhist.size()/QUESTIONS_PER_ROUND)*QUESTIONS_PER_ROUND;i<myhist.size();i++) {
	        					if(playedQuestions.get(i).equals(rand_question)) 
	        						{
	        							ok=true;
	        							break;
	        						}
	        				}
	        			}while(ok);
	        			rand_question.setCategory(cat);
	        			break;
	        		}
	        	}
        	}
        }
        
        addPlayedQuestion(rand_question);
        
        return rand_question;
    }
    
    /* set the answer of a player*/
    @Override
    public void setQuestionResult(String session_id, Question question, boolean correct, long time) {
        if(session_id.equals(playersId[0])) {
            historyPlayer0.add(new History(correct, time, question));
        }
        if(session_id.equals(playersId[1])) {
            historyPlayer1.add(new History(correct, time, question));
        }
    }
    
    /* return a playerstatus including correctness of played questions, round count, winner,
     *  rounds won, names and the right redirection. Includes also the computer player */
    @Override
    public PlayersStatus getStatus(String session_id, String ACTION, QuizFactory quizfactory) {
    	PlayersStatus out = quizfactory.createPlayersStatus();
    	
    	/* comp Opponent */
    	int i,k;
    	if(ACTION.equals(ACTION_ROUNDCOMPLETE) && (playersId[1].equals(NONE) || playersId[1].equals(COMP_PLAYER))) {
    		playersId[1]=COMP_PLAYER;
    		playersName[1]=COMP_PLAYER;
    		for(i=0;i<QUESTIONS_PER_ROUND;i++) {
    			int correct=Math.abs((new Random()).nextInt()%3);
    			/* 1/3 probability to answer correct if the Random function is equal distributed*/
    			Question q = this.getQuestionToPaly(COMP_PLAYER, quizfactory);
    			this.setQuestionResult(COMP_PLAYER, q, (correct==1)?true:false, Math.abs((new Random()).nextInt()%q.getMaxTime()));
    		}    		
    	}
    	    	
    	/* who is who */
    	List<History> myhist;
        List<History> opphist;
        if(session_id.equals(playersId[0])) {
            myhist=historyPlayer0;
            opphist=historyPlayer1;
        } else if(session_id.equals(playersId[1])) {
            myhist=historyPlayer1;
            opphist=historyPlayer0;
        } else return null;
        
        /* Right Redirection */ 
        if((myhist.size()>=ROUND_TO_PALY*QUESTIONS_PER_ROUND) && (opphist.size()>=ROUND_TO_PALY*QUESTIONS_PER_ROUND)) {
        	/* if game is not null terminated, terminate it*/
        	
        	/*show finishpage */
    		out.setNextPage(ACTION_FINISH);
    	} else if( ((myhist.size() % QUESTIONS_PER_ROUND)==QUESTIONS_PER_ROUND-1) ||
    			(((myhist.size() % QUESTIONS_PER_ROUND)==0) && opphist.size()<myhist.size()) ) {
    		/* if i answered tree questions or i'm on the roundcomplete page and my opponent is not there goto roundcomplete */
    		
    		/*show roundcomplete */
    		out.setNextPage(ACTION_ROUNDCOMPLETE);
    	}else {
    		/*show game */
    		out.setNextPage(ACTION_GAME);
    	}
        
        /* Names */
        out.setPlayer0Name(playersName[0]);
        out.setPlayer1Name(playersName[1]);
        
		/* rounds won + roundcount + winner*/
		int p0_round_count=0;
		int p1_round_count=0;
		if(ACTION.equals(ACTION_ROUNDCOMPLETE) || ACTION.equals(ACTION_FINISH)) {
			int p0_count=0;
			int p1_count=0;
			long p0_time=0;
			long p1_time=0;
			int limit;
			if(historyPlayer0.size()>=QUESTIONS_PER_ROUND*ROUND_TO_PALY && historyPlayer1.size()>=QUESTIONS_PER_ROUND*ROUND_TO_PALY)
				/* make final status */
				limit=QUESTIONS_PER_ROUND*ROUND_TO_PALY;
			else if(historyPlayer0.size()==historyPlayer1.size()) limit=historyPlayer0.size(); /* make roundcomplete status */
			else {
				/* not necessary stuff, but no time to remove and test */
				if(historyPlayer0.size()>historyPlayer1.size()) limit=historyPlayer0.size();
				else limit=historyPlayer1.size();
				if(limit<QUESTIONS_PER_ROUND) limit=0;
				else limit=limit-QUESTIONS_PER_ROUND;
			}
			for(i=0;i<limit;i++) {
				/* goes through history and calcs  rounds won + roundcount + winner  */
				if(historyPlayer0.get(i).isCorrect()) {
					p0_count++;
				}
				p0_time+=historyPlayer0.get(i).getTime();
				if(historyPlayer1.get(i).isCorrect()) {
					p1_count++;
				}
				p1_time+=historyPlayer1.get(i).getTime();
				if((i % QUESTIONS_PER_ROUND)==QUESTIONS_PER_ROUND-1) {
					if(p0_count==p1_count) {
						if(p0_time<p1_time) { p0_round_count++;	out.setWinner(0);
						}else if(p0_time==p1_time) out.setWinner(-1);
						else { p1_round_count++;	out.setWinner(1); }
					}else {
						if(p0_count>p1_count) { p0_round_count++; out.setWinner(0); }
						else { p1_round_count++; out.setWinner(1); }
					}
					p0_count=0;p1_count=0;
					p0_time=0;p1_time=0;
					out.setRoundCount(out.getRoundCount()+1);
				}
			}	
			/* if no roundcomplete for both no winner */
			if(historyPlayer0.size()!=historyPlayer1.size() && !ACTION.equals(ACTION_FINISH)) out.setWinner(-1);
			if(ACTION.equals(ACTION_FINISH)) {
				/* set game winner */
				if(p0_round_count>p1_round_count) out.setWinner(0);
				else out.setWinner(1);
			}
		}
		out.setPlayer0WonRounds(p0_round_count);
		out.setPlayer1WonRounds(p1_round_count);
		
		/* status */
        Status[] mystatus = new Status[QUESTIONS_PER_ROUND];
        Status[] oppstatus = new Status[QUESTIONS_PER_ROUND];
        int round;
        /* calc round */
        if(ACTION.equals(ACTION_ROUNDCOMPLETE)) round = (int)Math.ceil(myhist.size()/(QUESTIONS_PER_ROUND+1));
        else round = (int)Math.ceil(myhist.size()/QUESTIONS_PER_ROUND);
    	i=0;
    	do{
    		k=round*QUESTIONS_PER_ROUND+i;
    		if(myhist.size()>=k+1){
    			mystatus[i]=(myhist.get(k).isCorrect()) ? Status.CORRECT : Status.WRONG;
    		}else mystatus[i]=Status.NOT_SET;
    		i++;
    	}while(i<QUESTIONS_PER_ROUND);
    	if(myhist.size()==opphist.size() && ACTION.equals(ACTION_ROUNDCOMPLETE)) {
    		for(i=round*QUESTIONS_PER_ROUND;i<round*QUESTIONS_PER_ROUND+QUESTIONS_PER_ROUND;i++) {
    			oppstatus[i%QUESTIONS_PER_ROUND]=(opphist.get(i).isCorrect()) ? Status.CORRECT : Status.WRONG;
    		}
    	}else for(i=0;i<QUESTIONS_PER_ROUND;i++)oppstatus[i]=Status.NOT_SET; 
    	if(session_id.equals(playersId[0])) {
    		out.setPlayer0Status(mystatus);
    		out.setPlayer1Status(oppstatus);
    	} else if(session_id.equals(playersId[1])) {
    		out.setPlayer1Status(mystatus);
    		out.setPlayer0Status(oppstatus);
    	}
        
    	return out;
    }
    
    private List<String> getPlayedCategorys() {
    	List<String> out = new ArrayList<>();
    	for (Question q : playedQuestions) {
    		if(!out.contains(q.getCategory().getName()))
    			out.add(q.getCategory().getName());
    	}
    	
    	return out;
    }
    
    class History {
        private boolean correct;
        private long time;
        private Question question;

        public History(boolean correct, long time, Question question) {
            this.correct = correct;
            this.time = time;
            this.question = question;
        }

        public boolean isCorrect() {
            return correct;
        }

        public long getTime() {
            return time;
        }

        public Question getQuestion() {
            return question;
        }
        
    }
    
    /* devel */
    @Override
    public String toString() {
    	String out = "GAME: "+playersName[0]+" vs. "+playersName[1]+"\n";
    	
    	for(int i=0; i<QUESTIONS_PER_ROUND*ROUND_TO_PALY;i++){
    		out+=i+" :";
    		try{
    			out+=historyPlayer0.get(i).isCorrect();
    			out+="-"+historyPlayer0.get(i).getTime();
    		}catch(IndexOutOfBoundsException e){
    			out+="notplayed";
    		}
    		out+="   ";
    		try{
    			out+=historyPlayer1.get(i).isCorrect();
    			out+="-"+historyPlayer1.get(i).getTime();
    		}catch(IndexOutOfBoundsException e){
    			out+="notplayed";
    		}
    		out+="\n";
    	}
    	return out;
    			
    }

    
    
}

